package com.hotec.PROJECT_NAME.config.security.filter;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hotec.PROJECT_NAME.component.basic.ConstantKey;
import com.hotec.PROJECT_NAME.component.basic.JsonContent;
import com.hotec.PROJECT_NAME.component.enums.SmsEnum;
import com.hotec.PROJECT_NAME.component.exception.exc.BizException;
import com.hotec.PROJECT_NAME.config.security.token.AdminUsernamePasswordAuthenticationToken;
import com.hotec.PROJECT_NAME.config.security.token.MobileAndCodeToken;
import com.hotec.PROJECT_NAME.modules.sys.service.SysAccountService;
import com.hotec.PROJECT_NAME.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法
 * attemptAuthentication ：接收并解析用户凭证。
 * successfulAuthentication ：用户成功登录后，这个方法会被调用，我们在这个方法里生成token。
 *
 * @author
 */
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    public JwtLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    /**
     * 接收并解析用户凭证
     *
     * @param req
     * @param res
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
        try {
            Map<String, String> user = Utils3DES.decryptWithRequest(req, new TypeReference<Map<String, String>>() {
            });

            String msgCode = user.get(ConstantKey.LOGIN_MSG_CODE);

            String mobile = user.get("mobile");

            List<? extends GrantedAuthority> authorities = Lists.newArrayList();

            if(StrUtil.isNotBlank(msgCode)){
                boolean codeHasSuccess = UtilsSms.valid(mobile, msgCode, SmsEnum.LOGIN);

                if(codeHasSuccess) return authenticationManager.authenticate(new MobileAndCodeToken(mobile, authorities));

                throw new BizException("验证码错误");
            }

            String password = user.get("password");

            return authenticationManager.authenticate(new AdminUsernamePasswordAuthenticationToken(mobile, password, authorities));
        } catch (IOException e) {
            throw new BizException(e);
        }
    }

    /**
     * 登录成功产生token
     *
     * @param req
     * @param res
     * @param chain
     * @param auth
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req,
                                            HttpServletResponse res,
                                            FilterChain chain,
                                            Authentication auth) {
        // builder the token
        String token;
        try {
            LinkedHashMap<String, Object> tokenDataMap = Maps.newLinkedHashMap();
            tokenDataMap.put(ConstantKey.ACCOUNT_ID, auth.getName());
            tokenDataMap.put("roles", auth.getAuthorities());

            //TODO 需要改成字典项在后台开启
            String slp = UtilsSpring.getBean(Environment.class).getProperty("sing_login_place", "0");

            if (!StringUtils.equalsIgnoreCase(slp, ConstantKey.SING_LOGIN_PLACE)) {
                SysAccountService sysAccountService = UtilsSpring.getBean(SysAccountService.class);
                //更新用户最后登录时间
                sysAccountService.save(sysAccountService.get(auth.getName()).setLastLoginTime(new Date()));
                //直接取的值有问题，只能重新查询
                tokenDataMap.put("slp", sysAccountService.get(auth.getName()).getLastLoginTime().getTime());
            }

            token = UtilsJwt.createToken(tokenDataMap);
            HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
            objectObjectHashMap.put("key", ConstantKey.TOKEN_PRE + token);

            UtilsWeb.responseJson(res, JsonContent.success(objectObjectHashMap).setMessage("登录成功"));
        } catch (Exception e) {
            UtilsLog.log(this.getClass(), e);
        }
    }
}
